(* ===================================================================== *)
(** Application bopsimu : simulateur CDS à partir de fichiers .bop et .bpx
    @author Mathieu Barbin
    @version 1.0.* *)
(* ===================================================================== *)

open Traitement
open Inputtools

(* ===================================================================== *)
(** Soit on importe un fichier bpx resultant de la compilation d'une net
    list au format propretaire de bopsimu, soit d'une net-list bop
    directement. *)
type circuit_source = Bebop of string | Bebpx of string
(* bopsimu -c netlist.bop [-o netlist.bpx] (par defaut) *)
type mode_execution = Simulation | Compilation of string * string (* source * cible *)
(* ===================================================================== *)

(* ===================================================================== *)
(** Les parametres d'execution, (apres lecture de la ligne de commande *)
type execution = 
    {
      cycles : int option;
      typ : traitement_type;
      source : circuit_source;
      mode_execution : mode_execution
    }
(* ===================================================================== *)

(* ===================================================================== *)
(** Exeption levee quand la ligne de commande est incoherente *)
exception Arguments_Invalides
(* ===================================================================== *)

(* ===================================================================== *)
(** La chaine est-elle un entier ? *)
let is_an_int s = 
  try
    ignore (int_of_string s);
    true
  with
    | Failure _ -> false;;
(* ===================================================================== *)

(* ===================================================================== *)
(** Deduire d'un tableau Sys.argv le type d'execution
    Lever l'exeption Arguments_Invalides si la lecture echoue *)
(* ===================================================================== *)
let execution_of_argv argv =
  let len = Array.length argv in
  let exists e =
    let rec aux i =
      if i >= len
      then false
      else if argv.(i) = e then true else aux (succ i)
    in aux 1
  in
  begin
    if exists "-v"
    then Message.message#verbose_actif
    else Message.message#verbose_desactif;
    if exists "-c"
    then
      if len < 3 || len > 6 then raise Arguments_Invalides
      else
	let queue_sources = Queue.create ()
	and cible = ref "out.bpx"
	and i = ref 1 in
	begin
	  Message.message#verbose_actif;
	  while (!i < len) do
	    (
	      match argv.(!i) with
	      | "" -> ()
	      | "-o" -> (* lire le prochain mot qui est la cible *)
                  if (!i = pred len) 
                  then raise Arguments_Invalides 
                  else 
                    begin
                      incr i;
                      cible := argv.(!i)
                    end  
	      | m when (not(is_an_int m)) && (not(m.[0] = '-')) -> 
		  Queue.push m queue_sources
	      | o -> ()
	    );
	    incr(i)
	  done;
	  if Queue.is_empty queue_sources then raise Arguments_Invalides
	  else
	    let source = Queue.pop queue_sources in
	    {
	      cycles = None;
	      typ = Standard;
	      source = Bebop source;
	      mode_execution = Compilation (source, !cible)
	    }
	end
    else
      if len < 2 || len > 6 then raise Arguments_Invalides
      else
	let source = ref None
	and cycles = ref None
	and typ = ref Standard
	and i = ref 1 in
	begin
	  while (!i < len) do
	    (
	      match argv.(!i) with
	      | "" -> ()
	      | "-v" -> ()
	      | "-x" -> 
		  (* on precise que le prochain mot est un bpx *)
		  if (!i = pred len) 
                  then raise Arguments_Invalides 
                  else 
		    if !source <> None
		    then raise Arguments_Invalides
		    else 
                      begin
			incr i;
			source := Some (Bebpx argv.(!i))
                      end
	      | m when m.[0] = '-' -> 
		  if List.exists (fun (typ, _) -> typ = m) table_traitement
		  then typ := (List.assoc m table_traitement)
		  else raise Arguments_Invalides
	      | m when (is_an_int m) ->
		  if !cycles <> None 
		  then raise Arguments_Invalides 
		  else cycles := Some (int_of_string m)
	      | m -> 
		  if !source <> None
		  then raise Arguments_Invalides
		  else source := Some (Bebop m)
	    );
	    incr(i)
	  done;
	  match !source with
	  | None -> raise Arguments_Invalides
	  | Some src -> 
	      {
		cycles = !cycles;
		typ = !typ;
		source = src;
		mode_execution = Simulation
	      }
	end
  end
(* ===================================================================== *)

(* ===================================================================== *)
(** Construire un objet circuit à partir d'une net_list ou d'un fichier
    contenant les morceaux compilés (value) *)
(* ===================================================================== *)
let import_circuit = function
  | Bebop filename -> Cdsmaker.circuit_final_of_net_list filename
  | Bebpx filename -> 
      let filename, axiome, cds_final, rom_memory_tab, blocs_externes, entrees_formelles, sorties_formelles = Cdsmaker.serialisation_of_bpx_file filename in
      new Circuit.circuit filename axiome cds_final rom_memory_tab blocs_externes entrees_formelles sorties_formelles
(* ===================================================================== *)
 
(* ===================================================================== *)
(** Lancer la simulation avec les infos fournies par les options *)
(* ===================================================================== *)
let run_simu source traitement cycles =
  let c = import_circuit source in
  let i = new Inputmaker.get_input c#get_nom_entrees in
  let t = new_traitement_of_type traitement c#get_nom_entrees c#get_nom_sorties
  in
  Simulation.simulation traitement c i t cycles
(* ===================================================================== *)

(* ===================================================================== *)
(** Compiler une net-list vers un fichier cible bpx        *)
(* ===================================================================== *)
let compiler_circuit net cible = 
  let (filename, axiome, cds, rom, blocs, ent, sort) = Cdsmaker.serialisation_of_net_list net in
  let oc = open_out cible in
  output_value oc Cdsmaker.ident_bpx;
  output_value oc filename;
  output_value oc axiome;
  output_value oc cds;
  output_value oc rom;
  output_value oc blocs;
  output_value oc ent;
  output_value oc sort;
  close_out oc;
  Message.message#compilation_reussie net cible
(* ===================================================================== *)

let _ =
  try
    Message.message#thisis "bopsimu";
    let exe = execution_of_argv Sys.argv in
    begin
      match exe.mode_execution with
      | Simulation -> run_simu exe.source exe.typ exe.cycles
      | Compilation (bop, bpx) -> compiler_circuit bop bpx
    end
  with
  | Arguments_Invalides ->
      begin
        Printf.fprintf stderr "Simulation  .bop : %s [-traitement] [cycles] net_list.bop\n" Sys.argv.(0);
        Printf.fprintf stderr "Simulation  .bpx : %s [-traitement] [cycles] -x net_list.bpx\n" Sys.argv.(0);
        Printf.fprintf stderr "Compilation .bpx : %s -c [-o name (out.bpx default)] net_list.bop\n" Sys.argv.(0);
        exit 1
      end
